#include "renderer.h"
#include "gui.h"
#ifdef __APPLE__
	#include <OpenGL/gl.h>
#else
	#include <GL/gl.h>
#endif
#include <stdio.h>
#include "util/fontstash.h"
#include "../glapp.h"

GUI* gui;
event* guiEv;

#define BORDER_SIZE 4

const char* WidgetVSSource = {
    "#version 120\n\
    \n\
    void main()\n\
    {\n\
        gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;\n\
        gl_TexCoord[0] = gl_MultiTexCoord0;\n\
    }\n\
    "};

    
const char* WidgetFSSource = {
    "#version 120\n\
    uniform vec4 fillColor /*= vec4( 1.0, 0.0,0.0,1.0)*/;\n\
    uniform vec4 borderColor /*= vec4( 1.0, 1.0,1.0,1.0)*/;\n\
    uniform vec2 zones;\n\
    \n\
    void main()\n\
    {\n\
        float doTurn = float(gl_TexCoord[0].y > 0);\n\
        float radiusOffset = doTurn * abs( gl_TexCoord[0].z );\n\
        float turnDir = sign( gl_TexCoord[0].z );\n\
        vec2 uv = vec2(gl_TexCoord[0].x + turnDir*radiusOffset, gl_TexCoord[0].y);\n\
        float l = abs( length(uv) - radiusOffset );\n\
        float a = clamp( l - zones.x, 0.0, 2.0);\n\
        float b = clamp( l - zones.y, 0.0, 2.0);\n\
        b = exp2(-2.0*b*b);\n\
        gl_FragColor = ( fillColor * b + (1.0-b)*borderColor );\n\
        gl_FragColor.a *= exp2(-2.0*a*a);\n\
    }\n\
    "};

int initializeGUI(int w, int h){

	gui = malloc( sizeof(GUI));
	gui->widgetShader  = initializeShader( WidgetVSSource, WidgetFSSource );

	if (gui->widgetShader <=0 ){
		printf("Erro no widget shader: %d \n", gui->widgetShader);
		return -2;
	}

	gui->w = w;
	gui->h = h;
	gui->hotitem = gui->activeitem = gui->twophase = 0;

	gui->stash = sth_create(512, 512);

	//da pra desenhar a gui mesmo sem fonte
	if (!sth_add_font(gui->stash, 0, "data/fonts/DroidSerif-Regular.ttf")){
		printf("Font not found\n");
	}

	gui->widgetColor[0][0] = 89.0/255.0; gui->widgetColor[0][1] = 89.0/255.0; gui->widgetColor[0][2] = 89.0/255.0; gui->widgetColor[0][3] = 0.7;
	gui->widgetColor[1][0] = 166.0/255.0; gui->widgetColor[1][1] = 166.0/255.0; gui->widgetColor[1][2] = 166.0/255.0; gui->widgetColor[1][3] = 0.8;
	gui->widgetColor[2][0] = 212.0/255.0; gui->widgetColor[2][1] = 228.0/255.0; gui->widgetColor[2][2] = 60.0/255.0; gui->widgetColor[2][3] = 0.5;
	gui->widgetColor[3][0] = 227.0/255.0; gui->widgetColor[3][1] = 237.0/255.0; gui->widgetColor[3][2] = 127.0/255.0; gui->widgetColor[3][3] = 0.5;

	gui->borderColor[0][0] = 255.0/255.0; gui->borderColor[0][1] = 255.0/255.0; gui->borderColor[0][2] = 255.0/255.0; gui->borderColor[0][3] = 1.0;
	gui->borderColor[1][0] = 255.0/255.0; gui->borderColor[1][1] = 255.0/255.0; gui->borderColor[1][2] = 255.0/255.0; gui->borderColor[1][3] = 1.0;
	gui->borderColor[2][0] = 255.0/255.0; gui->borderColor[2][1] = 255.0/255.0; gui->borderColor[2][2] = 255.0/255.0; gui->borderColor[2][3] = 1.0;
	gui->borderColor[3][0] = 255.0/255.0; gui->borderColor[3][1] = 255.0/255.0; gui->borderColor[3][2] = 255.0/255.0; gui->borderColor[3][3] = 1.0;

	gui->fontColor[0][0] = 255.0/255.0; gui->fontColor[0][1] = 255.0/255.0; gui->fontColor[0][2] = 255.0/255.0; gui->fontColor[0][3] = 1.0;
	gui->fontColor[1][0] = 255.0/255.0; gui->fontColor[1][1] = 255.0/255.0; gui->fontColor[1][2] = 255.0/255.0; gui->fontColor[1][3] = 1.0;
	gui->fontColor[2][0] = 255.0/255.0; gui->fontColor[2][1] = 255.0/255.0; gui->fontColor[2][2] = 255.0/255.0; gui->fontColor[2][3] = 1.0;
	gui->fontColor[3][0] = 255.0/255.0; gui->fontColor[3][1] = 255.0/255.0; gui->fontColor[3][2] = 255.0/255.0; gui->fontColor[3][3] = 1.0;

	return 1;
}

void beginGUI(event* e){

	gui->hotitem = 0;
	gui->kbditem = 0;
	gui->lastwidget = 0;

	guiEv = e;

	glPushAttrib( GL_STENCIL_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT );
	glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
	
	glDisable(GL_STENCIL_TEST);
	glStencilMask( 0 );
	glDisable(GL_DEPTH_TEST);
	glDepthMask( GL_FALSE );

	glEnable(GL_BLEND);
	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

//	glColorMask( GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE );

	glMatrixMode( GL_MODELVIEW );
        glPushMatrix();
        glLoadIdentity();

	//coloca no modo ortho
	glMatrixMode( GL_PROJECTION );
    	glPushMatrix();
        glLoadIdentity();
	glOrtho( 0, gui->w, 0, gui->h, -1.0, 1.0);

}

void endGUI(){

	glDisable(GL_BLEND);
	glPopAttrib();
	glMatrixMode( GL_PROJECTION);
	glPopMatrix();
	glMatrixMode( GL_MODELVIEW);
	glPopMatrix();
}

//testa se um ponto ta dentro de um rect
int insideRect(rect* r, int x, int y){
	y = gui->h -y;
	int a = (x  >= r->x) && (x < r->x + r->w) && (y >= r->y) && (y < r->y + r->h ); 
	return (x  >= r->x) && (x < r->x + r->w) && (y >= r->y) && (y < r->y + r->h );
}

//verifica se esta na area do widget
int isHover(rect *r){

	if (gui->twophase) return  insideRect(r, gui->focusx, gui->focusy );
	return insideRect(r, guiEv->x, guiEv->y);

}

void drawRect(rect* r, int fillColor, int borderColor){

	bindShader(gui->widgetShader);	
	setShaderConstant4f(gui->widgetShader, "fillColor", gui->widgetColor[fillColor]);
	setShaderConstant4f(gui->widgetShader, "borderColor", gui->borderColor[borderColor] );
	float zones[] = {0.0, 0.0};
	setShaderConstant2f(gui->widgetShader, "zones", zones);

	float x0 = r->x;
	float x1 = r->x + r->w;
	        
	float y0 = r->y;
	float y1 = r->y + r->h;

	glBegin(GL_TRIANGLE_STRIP);
		glTexCoord2f(0, 0);
		glVertex2f( x0, y0);
		glVertex2f( x1, y0);
		glVertex2f( x0, y1);
		glVertex2f( x1, y1);
	glEnd();
}

void drawRoundedRect( rect* rect,  point* corner, int fillColorId, int borderColorId ){

	setShaderConstant4f(gui->widgetShader, "fillColor",  gui->widgetColor[fillColorId]);
	setShaderConstant4f(gui->widgetShader, "borderColor", gui->borderColor[borderColorId] );
	float zones[2]; zones[0] = corner->x - 1; zones[1] = corner->x-2;
	setShaderConstant2f(gui->widgetShader, "zones", zones);
	bindShader(gui->widgetShader);


    float xb = corner->x;
    float yb = corner->y;

    float x0 = rect->x;
    float x1 = rect->x + corner->x;
    float x2 = rect->x + rect->w - corner->x;
    float x3 = rect->x + rect->w;
    
    float y0 = rect->y;
    float y1 = rect->y + corner->y;
    float y2 = rect->y + rect->h - corner->y;
    float y3 = rect->y + rect->h;

    glBegin(GL_TRIANGLE_STRIP);
        glTexCoord2f(xb, yb);
        glVertex2f( x0, y0);
        glTexCoord2f(0, yb);
        glVertex2f( x1, y0);

        glTexCoord2f(xb, 0);
        glVertex2f( x0, y1);
        glTexCoord2f(0, 0);
        glVertex2f( x1, y1);

        glTexCoord2f(xb, 0);
        glVertex2f( x0, y2);
        glTexCoord2f(0, 0);
        glVertex2f( x1, y2);

        glTexCoord2f(xb, yb);
        glVertex2f( x0, y3);
        glTexCoord2f(0, yb);
        glVertex2f( x1, y3);
    glEnd();
    glBegin(GL_TRIANGLE_STRIP);
        glTexCoord2f(0, yb);
        glVertex2f( x2, y0);
        glTexCoord2f(xb, yb);
        glVertex2f( x3, y0);

        glTexCoord2f(0, 0);
        glVertex2f( x2, y1);
        glTexCoord2f(xb, 0);
        glVertex2f( x3, y1);

        glTexCoord2f(0, 0);
        glVertex2f( x2, y2);
        glTexCoord2f(xb, 0);
        glVertex2f( x3, y2);
 
        glTexCoord2f(0, yb);
        glVertex2f( x2, y3);
        glTexCoord2f(xb, yb);
        glVertex2f( x3, y3);
    glEnd();
    glBegin(GL_TRIANGLE_STRIP);
        glTexCoord2f(0, yb);
        glVertex2f( x1, y0);
        glTexCoord2f(0, yb);
        glVertex2f( x2, y0);

        glTexCoord2f(0, 0);
        glVertex2f( x1, y1);
        glTexCoord2f(0, 0);
        glVertex2f( x2, y1);

        glTexCoord2f(0, 0);
        glVertex2f( x1, y2);
        glTexCoord2f(0, 0);
        glVertex2f( x2, y2);
 
        glTexCoord2f(0, yb);
        glVertex2f( x1, y3);
        glTexCoord2f(0, yb);
        glVertex2f( x2, y3);
    glEnd();

	bindShader(0);
}

void drawDownArrow( rect* rect, int width, int fillColorId, int borderColorId ){

    float offset = sqrt(2.0)/2.0 ;
   
    float xb = width;
    float yb = width;
    
    float xoff = offset * xb ;
    float yoff = offset * yb ;
    float xoff2 = offset * xb *2.0 ;
    float yoff2 = offset * yb *2.0;

    float x0 = rect->x + xoff2;
    float x1 = rect->x + rect->w * 0.5;
    float x2 = rect->x + rect->w - xoff2;

    float y0 = rect->y + rect->h * 0.1 + yoff2;
    float y1 = rect->y + rect->h * 0.6;

    
	setShaderConstant4f(gui->widgetShader, "fillColor", gui->widgetColor[fillColorId] );
	setShaderConstant4f(gui->widgetShader, "borderColor", gui->borderColor[borderColorId] );
	float zones[2]; zones[0] = xb - 1; zones[1] = xb-2;
	setShaderConstant2f(gui->widgetShader, "zones", zones);
	    bindShader(gui->widgetShader);

    glBegin(GL_TRIANGLE_STRIP);
        glTexCoord3f(-xb, -yb, 0);
        glVertex2f( x0, y1 + yoff2);
        glTexCoord3f(xb, -yb, 0);
        glVertex2f( x0 - xoff2, y1);
 
        glTexCoord3f(-xb, 0, 0);
        glVertex2f( x0 + xoff, y1 + yoff);
        glTexCoord3f(xb, 0, 0);
        glVertex2f( x0 - xoff, y1 - yoff);

        glTexCoord3f(-xb, 0, xb);
        glVertex2f( x1, y0 + yoff2);
        glTexCoord3f(xb, 0, xb);
        glVertex2f( x1 - xoff2, y0);

        glTexCoord3f(xb, 2*yb, xb);
        glVertex2f( x1, y0 - yoff2);

    glEnd();
    glBegin(GL_TRIANGLE_STRIP);
        glTexCoord3f(xb, -yb, 0);
        glVertex2f( x2 + xoff2, y1);
        glTexCoord3f(-xb, -yb, 0);
        glVertex2f( x2, y1 + yoff2);

        glTexCoord3f(xb, 0, xb);
        glVertex2f( x2 + xoff, y1 - yoff);
        glTexCoord3f(-xb, 0, xb);
        glVertex2f( x2 - xoff, y1 + yoff);

        glTexCoord3f(xb, 0, xb);
        glVertex2f( x1 + xoff2, y0);
        glTexCoord3f(-xb, 0, xb);
        glVertex2f( x1, y0 + yoff2);

        glTexCoord3f(xb, 2*yb, xb);
        glVertex2f( x1, y0 - yoff2);

    glEnd();

    bindShader(0);
}

void drawFrame(rect* rect, point corner, int isHover, int  isDown){

	int color =  (isHover) + (isDown << 1);

	if (corner.x + corner.y == 0)
        	drawRect( rect , color, 0);
	else
		drawRoundedRect( rect, &corner , color, 0);

}

float getTextLineWidth(char* text){

	float minx, maxx, miny, maxy, totalwidth;
	sth_dim_text(gui->stash, 0, 20.0, text, &minx, &miny, &maxx, &maxy, &totalwidth  );

	return totalwidth;

}

float getFontHeight(){
	float ascender, descender, lineh;
	sth_vmetrics(gui->stash, 0, 20.0, &ascender, &descender, &lineh );
	return lineh;
}

void doLabel( rect textRect, char* text ){
	sth_begin_draw(gui->stash);
	glColor4fv(gui->fontColor[0]);
	sth_draw_text(gui->stash, 0, 20.0, textRect.x, textRect.y, text, &textRect.x);
	sth_end_draw(gui->stash);
}


int doButton(int  id, rect* r, char* text){

	//calcula a posicao do textRect baseado na fonte e na posicao inicial do rect
	rect textRect;
	textRect.x = r->x + BORDER_SIZE;
	textRect.y = r->y + BORDER_SIZE + 3;
	textRect.w = getTextLineWidth(text) + BORDER_SIZE*2;
	textRect.h = getFontHeight();

	//calcula o height e width do rect do button com base no texto
	r->h = textRect.h + 2*BORDER_SIZE;
	r->w = textRect.w + 2*BORDER_SIZE;

	int hover = isHover(r);
	if (hover){
		gui->hotitem = id;
		if (guiEv->buttonLeft && (gui->activeitem == 0)){
			gui->activeitem = id;
		}
	}

	point p;
	p.x = 4;
	p.y = 4;

	drawFrame(r, p, hover, (gui->activeitem == id));
	doLabel(textRect, text);

//	se o mouse ta up mas o item da hot e active entao o cara clicou
	if( !guiEv->buttonLeft && (gui->hotitem == id) && (gui->activeitem == id) ){
		gui->activeitem = 0;
		return 1;
	}

	return 0;
}

void drawBoolFrame( rect* r, point* p, int isHover, int isDown){
	int color =  (isHover) + (isDown << 1);
	drawRoundedRect( r, p , color, 0 );

}

//state eh passado por referencia, caso o botao seja clicado ele fica em 1, caso o contrario em 0
// cada checkbutton precisa do seu proprio state
int doCheckButton(int id, rect  *r, char* text, int * state){

	rect rText, rCheck;

	int rcOffset = (int) 0.125*16;
	rCheck.h = 16 - 2*rcOffset;
	rCheck.w = rCheck.h; //quadrado
	rCheck.x = BORDER_SIZE + rcOffset + r->x;
	rCheck.y = BORDER_SIZE + rcOffset + r->y;

	rText.x = 16 + 2*BORDER_SIZE + r->x;
	rText.y = BORDER_SIZE + r->y;
	rText.w = getTextLineWidth(text) + 2*BORDER_SIZE;
	rText.h = getFontHeight() + BORDER_SIZE; 

	point p;
	p.x = rCheck.w/6;
	p.y = rCheck.h/6;

	int hover = isHover(r);
	if (hover){
		gui->hotitem = id;
		if ( (guiEv->buttonLeft) && (gui->activeitem == 0)){
			gui->activeitem = id;
		}
	}

	drawBoolFrame(&rCheck, &p, hover, (state) && (*state));
	doLabel(rText, text);

	if( !guiEv->buttonLeft && (gui->hotitem == id) && (gui->activeitem == id) ){
		gui->activeitem = 0;
		*state = !*state;
		return 1;
	}

	return 0;

}

//no radiobutton os N botoes precisam do mesmo *state
int doRadioButton(int id, rect* r, char* text, int *state){

	rect rText, rCheck;

	int rcOffset = (int) 0.125*16;
	rCheck.h = 16 - 2*rcOffset;
	rCheck.w = rCheck.h; //quadrado
	rCheck.x = BORDER_SIZE + rcOffset + r->x;
	rCheck.y = BORDER_SIZE + rcOffset + r->y;

	rText.x = 16 + 2*BORDER_SIZE + r->x;
	rText.y = BORDER_SIZE + r->y;
	rText.w = getTextLineWidth(text) + 2*BORDER_SIZE;
	rText.h = getFontHeight() + BORDER_SIZE; 

	point p;
	p.x = rCheck.w/6;
	p.y = rCheck.h/6;


	int hover = isHover(r);
	if (hover)
		gui->hotitem = id;

	if ( (guiEv->buttonLeft) && hover){
		if (gui->activeitem == 0){
			gui->activeitem = id;
		}
	}

	drawBoolFrame(&rCheck, &p, hover, (state) && (*state == id));// diferente do checkbox
	doLabel(rText, text);

	if( !guiEv->buttonLeft && (gui->hotitem == id) && (gui->activeitem == id) ){
		gui->activeitem = 0;
		if (*state == id) //diferente do checkbox
			*state = 0;
		else
			*state = id;
		return 1;
	}
	return 0;
}

int doHorizontalSlider(int id, rect* r, float* value){

	float min = 0;
	float max = 1.0;

	//normaliza o valor
	float f = (*value - min) / (max - min);
	if (f < 0.0) f = 0.0;
	else if (f > 1.0) f = 1.0;

	rect rScroll, rCircle;

	r->w = 100 + 2*BORDER_SIZE + 12 ; //100 = tamanho base do scroll, 12 = tamanho do circulo
	r->h = 4 + 2*BORDER_SIZE;

	rScroll.x = BORDER_SIZE;
	rScroll.y = BORDER_SIZE;
	rScroll.h = 4; //altura do scroll
	rScroll.w = 100 + 12;

	rCircle.x = (int)(f*(rScroll.w));
	rCircle.h = rScroll.h*3;
	rCircle.y = rScroll.y - 4;
	rCircle.w = rCircle.h; //circulo quadrado
	

	int hover = isHover(r);
	if (hover)
		gui->hotitem = id;

	if ( (guiEv->buttonLeft) && hover){

		if (gui->activeitem == 0){
			gui->activeitem = id;
		}

		int xs = r->x + BORDER_SIZE;// + rCircle.w/2;
		int x = guiEv->x - xs;

		if (x < 0) 
			x = 0;
		else if (x > rScroll.w ) 
			x = rScroll.w ;

		rCircle.x = x;

		float f = (float) (x) / (float)(rScroll.w);
		f = f*(max - min);

		if (fabs(f - *value) > (max - min) * 0.001){
			*value = f;
		}
	}

	
	rScroll.x += r->x;	
	rScroll.y += r->y;
	rScroll.w += 6; // meio circulo
	point p;
	p.x = 2;
	p.y = 2;

	drawFrame(&rScroll, p, isHover, 0);

	rCircle.x += r->x;
	rCircle.y += r->y;
	p.x = rCircle.w/2;
	p.y = rCircle.h/2;
	drawFrame(&rCircle, p, 0, (gui->activeitem == id));

	if( !guiEv->buttonLeft && (gui->hotitem == id) && (gui->activeitem == id) ){
		gui->activeitem = 0;
	}


}

int doVerticalSlider(int id, rect* r, float* value){

	float min = 0;
	float max = 1.0;

	//normaliza o valor
	float f = (*value - min) / (max - min);
	if (f < 0.0) f = 0.0;
	else if (f > 1.0) f = 1.0;

	rect rScroll, rCircle;

	r->h = 100 + 2*BORDER_SIZE + 12 ; //100 = tamanho base do scroll, 12 = tamanho do circulo
	r->w = 4 + 2*BORDER_SIZE;

	rScroll.x = BORDER_SIZE;
	rScroll.y = BORDER_SIZE;
	rScroll.w = 4; //altura do scroll
	rScroll.h = 100 + 12;

	rCircle.y = (int)(f*(rScroll.h));
	rCircle.h = rScroll.w*3;
	rCircle.x = rScroll.y - 4;
	rCircle.w = rCircle.h; //circulo quadrado
	

	int hover = isHover(r);
	if (hover)
		gui->hotitem = id;

	if ( (guiEv->buttonLeft) && hover){

		if (gui->activeitem == 0){
			gui->activeitem = id;
		}

		int ys = r->y + BORDER_SIZE;
		int y = (gui->h - guiEv->y) - ys;

		if (y < 0) 
			y = 0;
		else if (y > rScroll.h ) 
			y = rScroll.h;

		rCircle.y = y;

		float f = (float) (y) / (float)(rScroll.h);
		f = f*(max - min);

		if (fabs(f - *value) > (max - min) * 0.001){
			*value = f;
		}
	}

	
	rScroll.x += r->x;	
	rScroll.y += r->y;
	rScroll.h += 6; // meio circulo
	point p;
	p.x = 2;
	p.y = 2;

	drawFrame(&rScroll, p, isHover, 0);

	rCircle.x += r->x;
	rCircle.y += r->y;
	p.x = rCircle.w/2;
	p.y = rCircle.h/2;
	drawFrame(&rCircle, p, 0, (gui->activeitem == id));

	if( !guiEv->buttonLeft && (gui->hotitem == id) && (gui->activeitem == id) ){
		gui->activeitem = 0;
	}


}

